package com.rm.zeyou.util.distance;

import lombok.experimental.UtilityClass;
import org.gavaghan.geodesy.Ellipsoid;
import org.gavaghan.geodesy.GeodeticCalculator;
import org.gavaghan.geodesy.GeodeticCurve;
import org.gavaghan.geodesy.GlobalCoordinates;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.Map;

/**
 * 计算两个地理坐标点之间的距离。
 */
@UtilityClass
public class MathDistanceUtil {
    //地球的半径
    private static final double EARTH_RADIUS = 6371000;
    private static final double DEGREES_TO_RADIANS = 0.017453292519943295;

//    /**
//     * 计算地球上两点之间的距离
//     * <p>公式：S=R·arccos[cosβ1·cosβ·2cos(α1-α2)+sinβ1·sinβ2]
//     * 计算次数多 此方法快
//     * @param longitude1 第一个点的经度。
//     * @param latitude1  第一个点的纬度。
//     * @param longitude2 第二个点的经度。
//     * @param latitude2  第二个点的纬度。
//     * @return 两点之间的距离，单位为米。
//     */
//    public static String getDistance(double longitude1, double latitude1, double longitude2, double latitude2) {
//        // Validate input to ensure it's within the valid range for coordinates
//        validateCoordinate(longitude1, latitude1);
//        validateCoordinate(longitude2, latitude2);
//
//        double radiansLongitude1 = Math.toRadians(longitude1);
//        double radiansLatitude1 = Math.toRadians(latitude1);
//        double radiansLongitude2 = Math.toRadians(longitude2);
//        double radiansLatitude2 = Math.toRadians(latitude2);
//
//        double cos = Math.cos(radiansLatitude1) * Math.cos(radiansLatitude2) *
//                Math.cos(radiansLongitude1 - radiansLongitude2) +
//                Math.sin(radiansLatitude1) * Math.sin(radiansLatitude2);
//
//        // Ensure the result of acos is valid
//        if (cos > 1) cos = 1;
//        else if (cos < -1) cos = -1;
//        double distance = EARTH_RADIUS * Math.acos(cos);
//        BigDecimal distanceBig = new BigDecimal(distance).setScale(2, RoundingMode.UP);
//        //BigDecimal distanceBig = new BigDecimal(distance).divide(new BigDecimal(1000),2,RoundingMode.UP);
//        return distanceBig.toString();
//    }



    /**
     * 计算两个地理坐标点之间的距离。
     *  R 是地球半径，φ1 和 φ2 分别是两个地点的纬度，λ1 和 λ2 是两个地点的经度，d 是两点之间的距离。
     *  d = R * arccos(sin(φ1) * sin(φ2) + cos(φ1) * cos(φ2) * cos(λ1 - λ2))
     * 计算次数少 此方法快
     *    jar包
     *       <dependency>
     *          <groupId>org.gavaghan</groupId>
     *          <artifactId>geodesy</artifactId>
     *          <version>1.1.3</version>
     *       </dependency>
     * @param lonA 第一个点的经度
     * @param latA 第一个点的纬度
     * @param lonB 第二个点的经度
     * @param latB 第二个点的纬度
     * @param newScale 期望的距离精度（小数点后的位数）
     * @return 两个点之间的距离 单位为米。
     * @throws IllegalArgumentException 如果经纬度超出有效范围或`newScale`小于0
     */
    public static String getDistance(Double lonA, Double latA, Double lonB, Double latB, int newScale) throws IllegalArgumentException {
        // 参数验证
        validateCoordinate(lonA, latA);
        validateCoordinate(lonB, latB);
        if (newScale < 0) {
            throw new IllegalArgumentException("位数不能小于0");
        }

        GlobalCoordinates source = new GlobalCoordinates(latA, lonA);
        GlobalCoordinates target = new GlobalCoordinates(latB, lonB);
        GeodeticCurve geoCurve = new GeodeticCalculator().calculateGeodeticCurve(Ellipsoid.Sphere, source, target);
        double distance = geoCurve.getEllipsoidalDistance();
        BigDecimal distanceBig = new BigDecimal(distance).setScale(newScale, RoundingMode.UP);
//        BigDecimal distanceBig = new BigDecimal(distance).divide(new BigDecimal(1000),newScale,RoundingMode.UP);
        return distanceBig.toString();
    }

    public static void main(String[] args) {
//        String distance = getDistance("108.947825,34.312953", "108.947337,34.317871", 2);
        String distance = getDistance("108.940000,34.341101", "108.954783,34.299317", 2);
        //if (Double.parseDouble(distance)<1000){
            System.out.println(distance);
        //}else {
        //    BigDecimal distanceBig = new BigDecimal(distance).divide(new BigDecimal(1000),2,RoundingMode.HALF_UP);
        //    System.out.println(distanceBig);
        //}

    }

    /**
     * 计算两个地理坐标点之间的距离。
     *  R 是地球半径，φ1 和 φ2 分别是两个地点的纬度，λ1 和 λ2 是两个地点的经度，d 是两点之间的距离。
     *  d = R * arccos(sin(φ1) * sin(φ2) + cos(φ1) * cos(φ2) * cos(λ1 - λ2))
     * 计算次数少 此方法快
     *    jar包
     *       <dependency>
     *          <groupId>org.gavaghan</groupId>
     *          <artifactId>geodesy</artifactId>
     *          <version>1.1.3</version>
     *       </dependency>
     * @param lonLatA 第一个点的经纬度
     * @param lonLatB 第二个点的经纬度
     * @param newScale 期望的距离精度（小数点后的位数）
     * @return 两个点之间的距离 单位为米
     * @throws IllegalArgumentException 如果经纬度超出有效范围或`newScale`小于0
     */
    public static String getDistance(String lonLatA, String lonLatB, int newScale) throws IllegalArgumentException {
        // 参数验证
        Map<String, Double> mapA = splitLonLat(lonLatA);
        Map<String, Double> mapB = splitLonLat(lonLatB);
        if (newScale < 0) {
            throw new IllegalArgumentException("位数不能小于0");
        }

        GlobalCoordinates source = new GlobalCoordinates(mapA.get("lat"), mapA.get("lon"));
        GlobalCoordinates target = new GlobalCoordinates(mapB.get("lat"), mapB.get("lon"));
        GeodeticCurve geoCurve = new GeodeticCalculator().calculateGeodeticCurve(Ellipsoid.Sphere, source, target);
        double distance = geoCurve.getEllipsoidalDistance();
//        BigDecimal distanceBig = new BigDecimal(distance).divide(new BigDecimal(1000),newScale,RoundingMode.UP);
        BigDecimal distanceBig = new BigDecimal(distance).setScale(newScale,RoundingMode.UP);
        return distanceBig.toString();
    }

    /**
     * 验证给定的经纬度是否在有效范围内。
     *
     * @param lon 经度
     * @param lat 纬度
     * @throws IllegalArgumentException 如果经纬度超出有效范围
     */
    private static void validateCoordinate(Double lon, Double lat) throws IllegalArgumentException {
        if (lon == null || lat == null) {
            throw new IllegalArgumentException("经纬度不能为null");
        }
        if (lon < -180 || lon > 180 || lat < -90 || lat > 90) {
            throw new IllegalArgumentException("经纬度超出有效范围");
        }
    }

    /**
     * 拆分给定的经纬度。
     *
     * @param lonLat 经纬度
     * @return  拆分后的经纬度
     */
    private static Map<String,Double> splitLonLat(String lonLat) throws IllegalArgumentException {
        if (lonLat == null || lonLat.equals("")) {
            throw new IllegalArgumentException("经纬度不能为null");
        }
        HashMap<String, Double> hashMap = new HashMap<>();
        double lon = Double.parseDouble(lonLat.split(",")[0]);
        double lat = Double.parseDouble(lonLat.split(",")[1]);
        validateCoordinate(lon, lat);
        hashMap.put("lon",lon);
        hashMap.put("lat",lat);
        return hashMap;
    }
}
